Implementing Multiple File Uploads in ASP.NET Core MVC Using HTML5
TLDR
- Use the HTML5
<input type="file" multiple>attribute to support multiple file selection. - The frontend should use the
FormDataobject to encapsulate file data and listen for upload progress via Axios'sonUploadProgress. - When receiving parameters in the backend Controller, use the
IFormFileCollectiontype for model binding. - To adjust file upload size limits, use
FormOptionsfor global configuration or attributes like[RequestSizeLimit]for individual Action adjustments. - Be sure to pay attention to the
multipart/form-dataContent-Type setting; when usingFormData, the browser handles this automatically, and manual configuration is not recommended.
Frontend Implementation: FormData and Progress Listening
When to encounter this issue: When you need to upload multiple files asynchronously via AJAX and display a real-time upload progress bar.
In frontend implementation, the FormData object can be used to conveniently encapsulate multiple files into a Key/Value format. Through the Axios onUploadProgress event, you can obtain the loaded and total properties to calculate and update the progress bar value.
new Vue({
el: '#app',
data: {
formData: new FormData,
progressBarValue: 0
},
methods: {
handleFileChange(e) {
this.formData = new FormData();
for (let i = 0; i < e.target.files.length; i++) {
this.formData.append(e.target.id, e.target.files[i]);
}
},
handleSubmit() {
let config = {
onUploadProgress: progressEvent => {
this.progressBarValue = (progressEvent.loaded / progressEvent.total * 100 | 0);
}
};
axios.post('@Url.Action("Index3")', this.formData, config).then(response => {
alert(response.data.message);
}).catch(thrown => {
alert(thrown);
});
}
}
});Backend Processing: IFormFileCollection Model Binding
When to encounter this issue: When the backend needs to receive multiple files sent from the frontend and wants to perform model binding directly via a ViewModel.
In ASP.NET Core, multiple file uploads should be handled using the IFormFileCollection type. This simplifies the tedious process of manually manipulating Request.Files as was required in MVC 5.
public class IndexViewModel {
[DisplayName("Upload Files")]
[Required]
public IFormFileCollection Files { get; set; }
}
[HttpPost]
public async Task<IActionResult> Index3(IndexViewModel viewModel) {
if (!ModelState.IsValid) {
string message = ModelState.First(x => x.Value.Errors.Count > 0)
.Value?.Errors.FirstOrDefault()?.ErrorMessage;
return Ok(new { Message = message });
}
foreach (var formFile in viewModel.Files) {
if (formFile.Length > 0) {
var filePath = Path.GetTempFileName();
using var stream = System.IO.File.Create(filePath);
await formFile.CopyToAsync(stream);
}
}
return Ok(new { Message = "Upload successful" });
}Adjusting File Upload Size Limits
When to encounter this issue: When uploaded files exceed the default limit (e.g., the default 128MB), causing the server to reject the request.
To adjust upload limits, you can choose between global configuration or configuration for a specific Action.
Global Configuration
Adjust the Kestrel or IIS request size limits in Program.cs:
builder.Services.Configure<FormOptions>(x => {
x.MultipartBodyLengthLimit = long.MaxValue;
});
builder.WebHost.ConfigureKestrel(opt => {
opt.Limits.MaxRequestBodySize = long.MaxValue;
});
builder.Services.Configure<IISServerOptions>(options => {
options.MaxRequestBodySize = long.MaxValue;
});Attribute Configuration
Adjust limits for a specific Action; this setting has a higher priority than the global configuration:
[HttpPost]
[DisableRequestSizeLimit]
[RequestFormLimits(MultipartBodyLengthLimit = long.MaxValue)]
public async Task<IActionResult> Index(IndexViewModel viewModel) {
// ...
}DANGER
Notes
- Setting
long.MaxValueis for demonstration purposes only; please set the limit size according to your actual requirements. - The units for the settings above are in bytes.
- Attribute priority is higher than global configuration.
Change Log
- 2022-10-24 Initial documentation created.
